package com.example.websocket_service.controller;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.websocket_service.config.FtpUtil;
import com.example.websocket_service.config.RestResponse;
import com.example.websocket_service.config.SnowflakeIdWorker;
import com.example.websocket_service.entity.Message;
import com.example.websocket_service.entity.emuns.UserTypeEnum;
import com.example.websocket_service.entity.emuns.WebSocketEnum;
import com.example.websocket_service.entity.form.MessageInfoF;
import com.example.websocket_service.entity.vo.MessageInfoV;
import com.example.websocket_service.service.MessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import static com.example.websocket_service.config.FtpUtil.*;
import static com.example.websocket_service.entity.emuns.UserTypeEnum.MESSAGEPICTYPE;

@Slf4j
@ServerEndpoint(value = "/websocket/{info}")
@Component

public class WebSocketController {

//    @Value("${files.upload.path}")  // spel表达式
    private String fileUploadPath;

    private static MessageService messageService;


    //    private RedisTemplate redisTemplate = SpringUtils.getBean("redisTemplate");    //获取redis实例
    private static RedisTemplate redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        WebSocketController.redisTemplate = redisTemplate;
    }

    @Autowired
    public void setMessageService(MessageService messageService) {
        WebSocketController.messageService = messageService;
    }


    private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


    //使用ConcurrentHashMap是为了保证线程安全，HashMap在多线程的情况下会出现问题
    private static ConcurrentHashMap<String, ConcurrentHashMap<String, WebSocketController>> roomList = new ConcurrentHashMap<>();

    // 与某个客户端的连接会话，需要通过他来给客户端发送消息

    private Session session;

    //重新加入房间标识（已加入时修改为1）

    private int rejoin = 0;

//    private int waitCustomer = 0;
//
//    private int waitTime = 0;

    //等待人数
    private static AtomicInteger waitCustomer = new AtomicInteger();
    //等待時間
    private static AtomicInteger waitTime = new AtomicInteger();

    //info 格式： 标识,房间名,用户名,用户类型（保证唯一性）
    //flag 分类：exitRoom 离开 joinRoom 加入房间 chatRoom 聊天 createRoom 创建房间
    //入参：info：{flag分类，房间id，用户类型，客户姓名}
    @OnOpen
    public void onOpen(@PathParam("info") String param, Session session) {
        log.info("info："+param);
        this.session = session;
        String flag = param.split("[,]")[0];
        String roomId = param.split("[,]")[1];
        Integer userType = Integer.valueOf(param.split("[,]")[2]); //userType为1表示客户，为2表示客服
        String userName = param.split("[,]")[3];
        if (WebSocketEnum.JOINROOM.getCode().equals(flag)&& UserTypeEnum.CUSTOMERTYPE.getCode().equals(userType)) {
            //1、客户创建房间
            this.joinRoom(roomId, userName,userType);
            //2、查询客户的聊天记录
//            List<Message> messageList = messageService.getMessages(userName);
            String key = "wait_" + roomId+"_"+userName;
            Date date = new DateTime();
            //3、在redis中存储现在等待的用户数和等待时间
//            redisTemplate.opsForValue().set(key, date, 60, TimeUnit.MINUTES);
//            redisTemplate.opsForSet().add(key,date);
            redisTemplate.opsForZSet().add(key, userName, date.getTime());
            //获取redis中所有的key，并设置等待时间
            Set<String> keys = redisTemplate.keys("*");
            List<String> values = redisTemplate.opsForValue().multiGet(keys);
//            waitCustomer = values.size();
            waitCustomer.set(values.size());
            //设置等待时间
//            waitTime = waitCustomer * 3;
            waitTime.set(waitCustomer.get()*3);
            //4、返回聊天记录、用户数和房间号
//            Map<String, Object> customerMap = new HashMap(100);
//            customerMap.put("messageList", messageList);
//            customerMap.put("roomId", roomId);
//            customerMap.put("userName", userName);
//            customerMap.put("waitCustomer", waitCustomer);
//            customerMap.put("waitTime", waitTime);
            MessageInfoV infoV = new MessageInfoV();
            infoV.setWaitCustomer(waitCustomer.intValue());
            infoV.setWaitTime(waitTime.intValue());
            infoV.setRoomId(roomId);
            infoV.setUserName(userName);
            // 消息发送
            sendMessage(infoV,session);
        } else {
            //5、客服根据房间号加入房间
            this.joinRoom(roomId, userName,userType);
            String createdUser = param.split("[,]")[4];
            //6、更新redis数据（等待用户数）
            String key = "wait_" + roomId+"_"+createdUser;
            redisTemplate.delete(key);
            waitMethod();
        }
    }

    //加入房间
    public void joinRoom(String roomId, String userName, Integer userType) {
        if (!roomList.containsKey(roomId) && UserTypeEnum.CUSTOMERSERVICETYPE.getCode().equals(userType)){
            return;
        }

        if (!roomList.containsKey(roomId) || UserTypeEnum.CUSTOMERTYPE.getCode().equals(userType)) {
            ConcurrentHashMap<String, WebSocketController> room = new ConcurrentHashMap<>();
            room.put(userName, this);
            roomList.put(roomId, room);
            Map<String, Object> map = new HashMap<>();
            map.put("flag", "createRoom");
            map.put("createdUser", userName);
            try {
                sendMessage(map,session);
            } catch (Exception e) {
                log.info("创建房间失败");
                e.printStackTrace();
            }
            log.info("欢迎" + userName + "创建房间"+roomId);
        } else {
            ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);
            if (room.get(userName) != null) {
                this.rejoin = 1;
            } else {
                room.put(userName, this);
                log.info("客服" + userName + "已加入房间"+roomId);
            }
            for (String item : room.keySet()) {
                Map<String, Object> map = new HashMap<>();
                map.put("flag", "joinRoom");
                map.put("name", "客服" + userName);
                map.put("status", "上线");
                //发送消息（排除自己）
                if (!Objects.equals(item,userName)){
                    room.get(item).sendMessage(map,room.get(item).session);
                }
//                room.get(item).sendMessage(map);
            }
        }
    }


    //    {"flag":"","roomId":"","userName":"","content":"","createdUser":""}
    //接收来自用户的消息
    @OnMessage
    public void onMessage (String message, Session session) {
        //7、开始聊天
//        JSONObject param = new JSONObject(message);
//        String flag = (String) param.get("flag");
//        String createdUser = (String) param.get("createdUser");
//        String roomId = (String) param.get("roomId");
//        String userName = (String) param.get("userName");
//        String fileName = (String) param.get("fileName");
//        String url = (String) param.get("url");
        final MessageInfoF param = JSONObject.parseObject(message, MessageInfoF.class);
        String userId = param.getUserId();
        String flag = param.getFlag();
        String roomId = param.getRoomId();
        String createdUser = param.getCreatedUser();
        String userName = param.getUserName();
        Integer userType = param.getUserType();
        Integer type = param.getType();


        if (WebSocketEnum.EXITROOM.getCode().equals(flag)) {
            ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);
            room.remove(userName);
            //判断房间中是否有人，如果没人则删除房间，否则通知其他用户该用户已经下线
            if (room.size() == 0) {
                roomList.remove(roomId);
            } else {
                for (String item : room.keySet()) {
                    param.setStatus("下线");
                    room.get(item).sendMessage(param,room.get(item).session);
                }
            }
            //如果是客户退出，需要更新redis
            if(UserTypeEnum.CUSTOMERTYPE.getCode().equals(param.getUserType())) {
                String key = "wait_" + roomId+"_"+createdUser;
                redisTemplate.delete(key);
            }
        } else if (WebSocketEnum.CHATROOM.getCode().equals(flag)) {
            param.setDate(df.format(new Date()));
            String content = param.getContent();
            ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);

//            if (room.size()>1){
//                roomList.remove(roomId);
//                for (String exRoom : roomList.keySet()) {
//                    for (String value : roomList.get(exRoom).keySet()) {
//                        Map<String, Object> params = new HashMap<>();
//                        params.put("waitCustomer",111111);
//                        params.put("waitTime", 111111);
//                        roomList.get(exRoom).get(value).sendMessage(params);
//                    }
//                }
//            }
//            try {
//                sendPic(url,roomId);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
            //单独处理图片

            if (MESSAGEPICTYPE.getCode().equals(type)) {
                try {
//                    sendPic(content, roomId, userName);
                    FTPClient ftpClient = new FTPClient();
                    // 2、指定服务器地址（端口）
                    ftpClient.connect(host, port);
                    // 3、指定账号和密码
                    ftpClient.login(username, password);
                    int reply = ftpClient.getReplyCode();
                    // 如果reply返回230表示成功，如果返回530表示无密码或用户名错误或密码错误或用户权限问题。
                    log.info("ftp连接成功: "+reply);
                    if (!FTPReply.isPositiveCompletion(reply)) {
                        ftpClient.disconnect();
                        log.info("ftp连接失败");
                    }
                    // 转移到FTP服务器目录
                    ftpClient.changeWorkingDirectory(remotePath);
//                    sendPic(content,roomId,userName);
                    sendPic(ftpClient,content,roomId,userName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (Objects.nonNull(room.get(userName))) {
                if (room.get(userName).rejoin == 0) {
                    //8、insert聊天记录
                    Message message1 = new Message();
                    BeanUtils.copyProperties(param, message1);
                    message1.setStatus(flag);
                    message1.setRoomId(Integer.valueOf(roomId));
                    message1.setCreatedTime(new DateTime().toString());
                    SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
                    long id = idWorker.nextId();
                    message1.setId(id);
                    messageService.insert(message1);
                    System.out.println("id"+message1.getId());
                    for (String item : room.keySet()) {

                        if (!Objects.equals(item, userName)) {
                            room.get(item).sendMessage(param, room.get(item).session);
                        }
                    }
                } else {
                    room.get(userName).sendMessage(param, session);
                }
            }
        }
    }

//    public void sendPicture(String fileName){
//        FileInputStream input;
//        try {
//            File file=new File("D:\\"+fileName);
//            input = new FileInputStream(file);
//            byte bytes[] = new byte[(int) file.length()];
//            input.read(bytes);
//            BinaryMessage byteMessage=new BinaryMessage(bytes);
//            session.sendFileMessage(byteMessage);
//            input.close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    public void  sendPic(String fileName,String roomId,String userName) throws Exception {
        log.info("【图片】推送文件名称:{}", fileName);
        ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);
        for (String item : room.keySet()) {
            FileInputStream input;
            try {
                //不同sid，获取的文件不一样,目前只区分：1和其他
//                String filePath="D:\\data\\images\\2022\\01\\13\\";
//                filePath=filePath+fileName+".jpg";
//                log.debug("【手动执行图片切换】图片路径：{}",filePath);
//                String filePath = "D:\\files\\6b479b567a854c0aa67e6101c445b438.webp";
                //读取文件
//                File file=new File(filePath);
                File file=new File(fileName);
                //生成字节数组
//                if(file==null || !file.exists()){
//                    log.debug("【手动执行图片切换】文件不存在，不执行消息推送");
//                    return;
//                }

                input = new FileInputStream(file);
                byte bytes[] = new byte[(int) file.length()];
                input.read(bytes);
                //转为ByteBuffer
//                BinaryMessage byteMessage=new BinaryMessage(bytes);
                if (!Objects.equals(item,userName)){
                    room.get(item).sendFileMessage(ByteBuffer.wrap(bytes));
                }

                input.close();
            } catch (IOException e) {
                log.error("消息发送失败" + e.getMessage(), e);
                return;
            }
        }
        log.debug("【图片】推送成功");
        return;
    }

    public void  sendPic(FTPClient ftpClient, String fileName, String roomId, String userName) throws IOException {
        log.info("【图片】推送文件名称:{}", fileName);
        ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);
        for (String item : room.keySet()) {
            FileInputStream input;
            try {
                //不同sid，获取的文件不一样,目前只区分：1和其他
//                String filePath="D:\\data\\images\\2022\\01\\13\\";
//                filePath=filePath+fileName+".jpg";
//                log.debug("【手动执行图片切换】图片路径：{}",filePath);
//                String filePath = "D:\\files\\6b479b567a854c0aa67e6101c445b438.webp";
                //读取文件
//                File file=new File(filePath);

                File file = new File(fileName);
                //生成字节数组
//                if(file==null || !file.exists()){
//                    log.debug("【手动执行图片切换】文件不存在，不执行消息推送");
//                    return;
//                }
                if (file.exists()) {
                    OutputStream is = new FileOutputStream(file);
                    //从服务器检索命名文件并将其写入给定的OutputStream
                    ftpClient.retrieveFile(fileName, is);
                    is.close();
                    input = new FileInputStream(file);
                    int available = input.available();
                    System.out.println(available + "======================");
                    byte bytes[] = new byte[(int) file.length()];
                    input.read(bytes);
                    int available1 = input.available();
                    System.out.println("-------------------------" + available1);
                    //转为ByteBuffer
//                BinaryMessage byteMessage=new BinaryMessage(bytes);
                    if (!Objects.equals(item, userName)) {
                        room.get(item).sendFileMessage(ByteBuffer.wrap(bytes));
                    }
                    input.close();
                }
            } catch (IOException e) {
                log.error("消息发送失败" + e.getMessage(), e);
                return;
            }
        }
        ftpClient.logout();
        log.debug("【图片】推送成功");
        return;
    }


    @OnClose
    public void onClose (@PathParam("info") String param, Session session){
        String roomId = param.split("[,]")[1];
        Integer userType = Integer.valueOf(param.split("[,]")[2]);
        String userName = param.split("[,]")[3];
        ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);
        if (Objects.nonNull(room)) {
            Map<String, Object> params = new HashMap<>();
            params.put("flag", "exitRoom");
            params.put("roomId", roomId);
            if (UserTypeEnum.CUSTOMERSERVICETYPE.getCode().equals(userType)) {
                params.put("nickname", "客服" + userName);
            } else {
                params.put("nickname", userName);
            }
            params.put("status", "下线");
            room.remove(userName);
            String key = "wait_" + roomId + "_" + userName;
            redisTemplate.delete(key);
            for (String item : room.keySet()) {
                room.get(item).sendMessage(params, room.get(item).session);
            }
            if (UserTypeEnum.CUSTOMERSERVICETYPE.getCode().equals(userType) && !CollectionUtils.isEmpty(room.keySet())) {
                String createdUser = param.split("[,]")[4];
                key = "wait_" + roomId + "_" + createdUser;
                redisTemplate.opsForZSet().add(key, createdUser, new DateTime().getTime());
                waitMethod();
            }
            if (room.size() == 0) {
                roomList.remove(roomId);
            }
        }
    }
    @OnError
    public void onError (@PathParam("info") String param, Throwable t, Session session){

        log.info("[WebSocketServer] Connection Exception : info = " + param + " , throwable = " + t.getMessage());
        log.info("session = " + session.toString());
    }

//    //发送消息
//    public void sendMessage(Map<String, Object> map,Session session){
//        String jsonObject = JSON.toJSONString(map);
//        //getAsyncRemote是非阻塞式的
////        this.session.getAsyncRemote().sendText(jsonObject);
//        synchronized(session){
//
//            session.getAsyncRemote().sendText(jsonObject);
//
//        }
//    }
    //发送消息(map格式)
    public void sendMessage(Object map, Session session) {
        String jsonObject = JSON.toJSONString(map);
    //getAsyncRemote是非阻塞式的
    //        this.session.getAsyncRemote().sendText(jsonObject);
        synchronized (session) {

            session.getAsyncRemote().sendText(jsonObject);

        }
    }


    //更新等待人数和等待时间的方法
    private void waitMethod() {
        Set<String> keys = redisTemplate.keys("*");
        List<String> values = redisTemplate.opsForValue().multiGet(keys);
        for (String exRoom : keys) {
            exRoom = exRoom.split("_")[1];
            for (String value : roomList.get(exRoom).keySet()) {

                Map<String, Object> params1 = new HashMap<>();
                params1.put("waitCustomer", values.size());
                params1.put("waitTime", values.size() * 3);
                roomList.get(exRoom).get(value).sendMessage(params1,roomList.get(exRoom).get(value).session);
            }
        }
    }

    /**
     * 主动推送到客户端，发文件流消息
     *
     * @param fileName 文件名称
     */
//    public void sendPic(String fileName,String roomId) throws Exception {
//        log.info("【手动执行图片切换】推送消息到窗口,sid：{}，推送文件名称:{}", fileName);
//        ConcurrentHashMap<String, WebSocketController> room = roomList.get(roomId);
//        for (String item : room.keySet()) {
//            try {
//                //不同sid，获取的文件不一样,目前只区分：1和其他
////                String filePath="D:\\data\\images\\2022\\01\\13\\";
////                filePath=filePath+fileName+".jpg";
////                log.debug("【手动执行图片切换】图片路径：{}",filePath);
//                String filePath = "D:\\files\\cf726969bfab4efd8beffa04044ebe7c.webp";
//                //读取文件
//                File file=new File(filePath);
//                //生成字节数组
////                if(file==null || !file.exists()){
////                    log.debug("【手动执行图片切换】文件不存在，不执行消息推送");
////                    return;
////                }
//
//                byte[] bytes=fileToByte(file);
//                //转为ByteBuffer
//                room.get(item).sendFileMessage(ByteBuffer.wrap(bytes));
//            } catch (IOException e) {
//                log.error("消息发送失败" + e.getMessage(), e);
//                return;
//            }
//        }
//        log.debug("【手动执行图片切换】推送成功");
//        return;
//    }

    public static byte[] fileToByte(File img) throws Exception {
        byte[] bytes = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            BufferedImage bi;
            bi = ImageIO.read(img);
            ImageIO.write(bi, "jpg", baos);
            bytes = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            baos.close();
        }
        return bytes;
    }

    /**
     * 服务器主动提推送消息
     *
     * @param fileByteBuffer 文件流内容
     * @throws IOException io异常抛出
     */
    public void sendFileMessage(ByteBuffer fileByteBuffer) throws IOException {
        log.debug("【手动执行图片切换】推送fileByteBuffer:{}",fileByteBuffer);
        this.session.getBasicRemote().sendBinary(fileByteBuffer);
    }



}
